package plus.lgx.textsearcher.searcher.printer;

import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import plus.lgx.textsearcher.entity.PrintData;

import java.io.PrintStream;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * HTML 格式输出的输出器<br>
 *
 * Create time: 12/22/22 9:07 PM<br>
 *
 * @author lgx
 * @version 1.0
 */
public class HTMLOutputHandler extends AbstractOutputHandler {

    private static final String HEAD_TEMPLATE = """
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <title>%s - search result</title>
                <style>
                    *{margin:0;padding:0;transition:background-color .2s,color .2s}a{color:#333}a:hover{color:#d98315}li::marker,li>div{font-family:'DejaVu Sans Mono'}html,body{height:100%%}ol{margin-left:60px}body>ol>li{margin-bottom:10px}ol ol{margin-left:20px}li li:hover{background-color:#f6f6f6}pre>span{font-weight:bold;background-color:yellow}pre span span{color:#600505}pre{white-space:break-spaces}
                </style>
            </head>
            <body>
                <ol>
            """;

    private static final String FILE_HEAD_TEMPLATE = """
                    <li>
                        <div><a href="file://%s">【%s】</a></div>
                        <ol>
            """;

    private static final String ITEM_TEMPLATE = """
                            <li><pre>%s</pre></li>
            """;

    private static final String FILE_TAIL_TEMPLATE = """
                        </ol>
                    </li>
            """;

    private static final String TAIL_TEMPLATE = """
                </ol>
            </body>
            </html>
            """;

    private static final Pattern P = Pattern.compile("[<>]");
    private static final Function<MatchResult, String> REPLACER = mr -> mr.group().equals("<") ? "&lt;" : "&gt;";

    private final Pattern pattern;

    private final Function<MatchResult, String> ITEM_REPLACER = mr -> {
        String group = mr.group();
        int c = mr.groupCount();
        if (c == 0) return span(Matcher.quoteReplacement(group));

        for (int index = 0; index < c; index++) {
            if (mr.group(index + 1) == null) continue;

            String[] arr = new String[c * 2 + 1];
            int end = 0;

            arr[0] = data.item.substring(mr.start(), Math.max(mr.start(), mr.start(1)));
            end = Math.max(end, mr.end(1));
            arr[1] = span(mr.group(1));
            for (int i = 1; i < c; i++) {
                int start;
                while ((start = mr.start(i + 1)) >= 0 && start < mr.end(i)) arr[i * 2] = arr[i++ * 2 + 1] = "";
                arr[i * 2] = -1 == start
                        ? "" : data.item.substring(end, start);
                if (mr.end(i + 1) != -1) end = mr.end(i + 1);
                arr[i * 2 + 1] = span(mr.group(i + 1));
            }
            arr[c * 2] = data.item.substring(end, mr.end());

            return span(Matcher.quoteReplacement(String.join("", arr)));
        }

        return span(Matcher.quoteReplacement(group));
    };

    private static String span(String s) {
        return StrUtil.isBlank(s) ? Optional.ofNullable(s).orElse("") : "<span>" + s + "</span>";
    }

    public HTMLOutputHandler(PrintData pd, PrintStream out, boolean ignoreCase) {
        super(pd, out);
        char[] chs = data.searchText.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (char ch : chs)
            sb.append(switch (ch) {
                case '{', '}', '[', ']', '(', ')', '+', '|',
                        '^', '$', '.', '?', '*', '\\' -> "\\" + ch;
                default -> ch;
            });
        this.pattern = Pattern.compile(sb.toString(), ignoreCase ? Pattern.CASE_INSENSITIVE : 0);
    }

    public HTMLOutputHandler(PrintData pd, PrintStream out, Pattern pattern) {
        super(pd, out);
        this.pattern = Pattern.compile(pattern.pattern(), pattern.flags());
    }

    @Override
    public void printHead() {
        out.format(HEAD_TEMPLATE, P.matcher(data.searchText).replaceAll(REPLACER));
    }

    @Override
    public void printTail() {
        out.print(TAIL_TEMPLATE);
    }

    @Override
    public void printFileHead() {
        out.format(FILE_HEAD_TEMPLATE, URLUtil.encode(data.filePath),
                P.matcher(FileNameUtil.mainName(data.fileName)).replaceAll(REPLACER));
    }

    @Override
    public void printFileTail() {
        out.print(FILE_TAIL_TEMPLATE);
    }

    @Override
    public void printItem() {
        data.item = P.matcher(data.item).replaceAll(REPLACER);
        out.format(ITEM_TEMPLATE, pattern.matcher(data.item).replaceAll(ITEM_REPLACER));
    }

    @Override
    public void close() {
        out.close();
    }
}
